home *** CD-ROM | disk | FTP | other *** search
/ HPAVC / HPAVC CD-ROM.iso / TPTUTR~1.ZIP / PASCAL08.TXT < prev    next >
Text File  |  1996-03-21  |  22KB  |  535 lines

  1.                         Turbo Pascal for DOS Tutorial
  2.                               by Glenn Grotzinger
  3.                  Part 8 -- DOS file functions (special topic 1)
  4.                All parts copyright (c) 1995 by Glenn Grotzinger
  5.  
  6. Here's a solution to part 7....
  7.  
  8. program part7;
  9.  
  10.   { demos 2 functions defined in part 7 to be written.  Convert base 10
  11.     to anybase, and convert anybase to base 10 }
  12.  
  13.   function power(int, ord: integer):longint;
  14.     { support function required for xbase2dec.  Simplistic
  15.       implementation of taking a power. }
  16.     var
  17.       i, endit: longint;
  18.     begin
  19.       endit := 1;
  20.       for i := 1 to ord do
  21.         endit := endit * int;
  22.       power := endit;
  23.     end;
  24.  
  25.   function dec2xbase(int: longint; base: integer):string;
  26.     { converts base 10 to any base < 37 }
  27.     const
  28.       numguide: string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  29.       { the guide string I mentioned as a hint }
  30.     var
  31.       i: integer;
  32.       hold, chkprod1, chkprod2: longint;
  33.       endstr: string;
  34.     begin
  35.       if base > 36 then
  36.         dec2xbase := '!' { a signal character to say our function failed }
  37.       else
  38.         begin
  39.           endstr := '';
  40.           hold := int;
  41.           while chkprod1 <> 0 do
  42.             begin
  43.               chkprod1 := hold div base; { using method described when I }
  44.               chkprod2 := hold mod base; { demoed doing it manually }
  45.               endstr := numguide[chkprod2 + 1] + endstr;
  46.                         { actual representation }
  47.               hold := chkprod1;
  48.             end;
  49.           dec2xbase := endstr;
  50.         end;
  51.     end;
  52.  
  53.   function xbase2dec(int: string; base: integer):longint;
  54.     { converts any base < 37 to base 10 }
  55.     const
  56.       numguide: string = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  57.     var
  58.       i, powr: integer;
  59.       endresult: longint;
  60.       intlength: integer;
  61.     begin
  62.       if base > 36 then
  63.         xbase2dec := -1 {signal fucntion has failed }
  64.       else
  65.         begin
  66.           endresult := 0;
  67.           i := 1;
  68.           powr := length(int) - 1;
  69.           while i <> length(int)+1 do   { compute back to base 10 }
  70.             begin
  71.               endresult := endresult + (pos(int[i], numguide)-1) *
  72.                            power(base, powr);
  73.               i := i + 1;
  74.               powr := powr - 1;
  75.             end;
  76.         end;
  77.       xbase2dec := endresult;
  78.     end;
  79.  
  80.   var
  81.     numread, numbase: longint;
  82.     convbase: string;
  83.   begin
  84.     write('Enter a number (base 10): ');
  85.     readln(numread);
  86.     write('What base do you want to convert to? ');
  87.     readln(numbase);
  88.     writeln;
  89.     convbase := dec2xbase(numread, numbase);
  90.     if convbase = '!' then
  91.       writeln('Use a base less than 37')
  92.     else begin
  93.       writeln(numread, ' (base 10) is ', convbase, ' (base ', numbase, ').');
  94.       numread := 0;
  95.   { required setting of initial read var to 0 to prove our functions work }
  96.       writeln('To check: ', convbase, ' (base ', numbase, ') is ',
  97.                xbase2dec(convbase, numbase), ' (base 10).');
  98.     end;
  99.   end.
  100.  
  101. Now on with the show....This is going to be more of a special topics practice
  102. thing.  As a note, all of the commands I use here will require you to use
  103. the dos or windos unit....also, have your TP Programmers Reference handy to
  104. look up several of the basic functions that I will list here.
  105.  
  106. A simple if successful...
  107. =========================
  108. How do we check if any of these, or our prior reset/rewrite calls are
  109. successful?  We toggle the I compiler option before a valid file oper-
  110. ator, then we check the variable in IOResult.  If IOResult <> 0, then
  111. we know there is a problem.  For example, how do we check if a file
  112. exists on the drive before we read from it?
  113.  
  114. function exists(filename: string):boolean;
  115.   var
  116.     ourfile: text; { can be defined to be any type we want }
  117.   begin
  118.     assign(ourfile, filename);  { let the program know about the file }
  119.     {$I-}reset(ourfile);{$I+}   { toggle I, on a reset. Both MUST be there }
  120.     if IOResult <> 0
  121.     { did something go wrong?  With reset, if file did not exist, something
  122.       would be wrong }
  123.       exists := false           { indicate file doesn't exist }
  124.     else  { file exists }
  125.       begin
  126.         close(ourfile); { we need to close the file since if it's there, }
  127.         exist := true;  { we just opened it.  Also, indicate it's there. }
  128.       end;
  129.   end;
  130.  
  131. This method can be used with any file function like that as long as the
  132. system is aware of the file, to indicate success, possibilities, etc.,
  133. in whatever logical means the command indicates.  For reset, one logically
  134. can say that if something goes wrong with it, the file isn't there, or the
  135. path is invalid.  For rewrite, one logically can say if the path isn't
  136. right or available, or if there's no disk space, or a variety of reasons.
  137.  
  138. The I compiler directive indicates turning ON or OFF input/output checking.
  139. The default for most compilers is {$I+}.  As compiler directives go, we
  140. must toggle it back, because the area after the change is in effect, and
  141. we only want it for the case of the command we want to check.  If I/O
  142. checking is on, the program would end in a run-time error (you may have
  143. noticed this, if you have tried to access non-existent files in your
  144. experimentation with reading/writing text files). If I/O checking is off
  145. ({$I-}, the result of the command is logged in a global variable named
  146. IOResult.
  147.  
  148. Being able to subvert run-time errors is good to be able to do, especially
  149. for a DOS file function type program, where you even deal with files.
  150. If the user specifies an incorrect file to read, you can return an intel-
  151. ligible, user-understandable error message that the filename they gave
  152. does not exist on the drive.
  153.  
  154. A list of run-time error possibilities may be found on page 260 of the
  155. TP7 programmers reference.
  156.  
  157. Straight Forward Things
  158. =======================
  159.         I will list several commands for working with files and DOS
  160. which are relatively straight-forward to use.  Unit used, then command,
  161. then a short description.  Detailed descriptions follow if needed.  I
  162. will mainly cover the DOS unit variants.  If you use TPW, look up the
  163. WinDOS variant equivalents...Be sure to look each of these up in any
  164. case.... By all means, play with each of these to understand them.
  165. Notes on some of these things will appear later.
  166.  
  167. System: ChDir(Str: string);  { changes the current directory to path in Str }
  168. DOS/WinDos: DiskFree(Drive: byte):longint; { free bytes on Drive }
  169. DOS/WinDos: DiskSize(Drive: byte):longint; { total bytes on Drive }
  170. DOS/WinDos: DosVersion: word; { tells us what version of DOS we have }
  171. DOS: EnvCount: integer; { how many environment strings? }
  172. DOS: EnvStr(index: integer): string; {return a specific environment string}
  173. System: erase(file: filetype);  {erases an external file}
  174. System: FilePos(file: filetype): longint; {returns current position in file}
  175. System: FileSize(file: filetype): longint; {returns a file's size}
  176. DOS: FSearch(filename: PathStr; dirlist: string):Pathstr {search for a file}
  177. DOS: FSplit(...  {split a filename into a dir, name, and ext}
  178. DOS/WinDos: Getdate(... { gets the current date of the operating system.}
  179. System:GetDir(drive; str: string); {gets current directory}
  180. DOS: GetEnv(...  {gets the specified environment variable}
  181. DOS/WinDos: GetFAttr (... { returns file attributes of a file }
  182. DOS/WinDos: GetFTime (... { get the date and time of a file }
  183. DOS/WinDos: GetTime(... { get current time }
  184. System: Halt(code); { quits program immediately with an errorlevel }
  185. System: MkDir(str: string); { makes a directory named str }
  186. DOS/WinDos: PackTime(... { packs a time/date }
  187. System: Rename(file: filetype); { renames an external file }
  188. System: RmDir(str: string); { removes a dir named str }
  189. System: Seek(file: filetype); { finds an element # in a file }
  190. DOS/WinDos: SetDate( ... { sets the date on the machine }
  191. DOS/WinDos: SetFTime(... { sets the date and time of a file }
  192. DOS/WinDos: SetTime(...  { set the time on the machine }
  193. DOS/WinDos: UnpackTime(... { unpacks a time/date to datetime record }
  194.  
  195. Notes on some of the commands listed above that aren't that self-
  196. explanatory
  197. ---------------------------------------------------------------------
  198. Diskfree(Drive: byte): longint;
  199. DiskSize(Drive: byte): longint;
  200.      Drive is a numerical byte: 0 is the current drive.
  201.                                 1 is A drive.
  202.                                 2 is B drive.
  203.                                 3 is C drive.
  204.                                 and so on and so forth.
  205.  
  206. NOTE: DiskFree and DiskSize are limited from what I understand to < 1 gig?
  207. (Please correct me if I am not quoting this right).  I know there is a
  208. limit there somewhere...
  209.  
  210. DosVersion(version: word);
  211.      You have to use HI and LO functions here as described in part 7.
  212.      The high order of this expression would be a minor revision number
  213.      and the low order of this expression would be a major revision
  214.      number.  For example, if you have DOS 6.20, the high order would be
  215.      20, and the low order would be 6.
  216.  
  217. Any expression that uses packed and unpacked date and time.
  218.      There is two of the functions listed above called packtime and
  219.      unpacktime.  There is a special record already defined in DOS
  220.      called DateTime (or TDateTime in WinDOS).  Both of these are defined
  221.      like this:
  222.  
  223.      DateTime { or TDateTime -- they're the same } = record
  224.         Year, Month, Day, Hour, Min, Sec: word;
  225.      end;
  226.      A packed time is stored as a longint;
  227.  
  228. Good use of an erase/rename, etc, etc...
  229.      Make a procedure that does the assign, and error checks, then do
  230.      the command, if the command requires the file to be an assigned
  231.      file variable.  For example:
  232.  
  233.      procedure deletefile(filename: string);
  234.        var
  235.          afile: text; { It can be anything we will find out }
  236.        begin
  237.          assign(afile, filename);
  238.          {$I-}erase(afile);{$I+}
  239.          if IOResult <> 0 then
  240.            writeln('Erasure unsuccessful.');
  241.          else
  242.            writeln(filename, ' has been erased from your drive!');
  243.        end;
  244.  
  245. Note: This erase command is recoverable by use of an undelete program.
  246. The rewrite is not (all you'll see is a 0 byte file that replaces the
  247. file you rewritten).
  248.  
  249. Parameter Passing
  250. =================
  251. You may have noted that we can get parameters in from the command-
  252. line to some programs we have used, such as PKZIP and PKUNZIP.
  253. We can write and design our programs to do such a similar thing.
  254.  
  255. Paramcount: integer;  { holds the number of command-line parameters used }
  256. Paramstr(num: integer): string { specific command-line parameter }
  257.  
  258. An example: Write a text file out to the screen using full error-checking,
  259. and taking the filename from the command-line.  Describes a command-line
  260. parameter describing a single file.
  261.  
  262. program typefile;
  263.   var
  264.     param1, instr: string;
  265.     thefile: text;
  266.   begin
  267.     if paramcount <> 1 then  { if there is not one command-line parameter }
  268.       begin
  269.         { show some help to the user }
  270.         halt(1);    { quit the program right here }
  271.       end;
  272.     param1 := paramstr(1); { corresponds to %1 in batch file processing }
  273.     { always good to do -- found that addressing the function directly as
  274.       a string causes problems }
  275.     assign(thefile, param1);
  276.     {$I-}reset(thefile);{$I+}
  277.     if IOResult <> 0 then
  278.       begin
  279.         writeln('This file doesn''t exist!');
  280.         halt(1);
  281.       end;
  282.     readln(thefile, instr);
  283.     { if the file is there, no need to error check reads, if our logic
  284.       is correct }
  285.     while not eof(thefile) do
  286.       begin
  287.         writeln(instr);
  288.         readln(thefile, instr);
  289.       end;
  290.     writeln(instr);
  291.   end.
  292.  
  293. Now, what if we want to determine a file based on a command-line parameter.
  294. This isn't exactly complete error checking, as we aren't processing whether
  295. the file we specify is a directory or not (EVERYTHING to DOS is a file of
  296. some sort, including a directory, we can't exactly TYPE a directory...).
  297. That was a demo of picking up command-line parameters, here's how we
  298. process a filename parameter so we are addressing the correct thing,
  299. EVEN a directory, and series of files (Remember wildcards in DOS?  The
  300. usage of * and ? in specification of files).
  301.  
  302. We use Fexpand, Fsplit, and GetFattr with doing this, as well as some DOS
  303. defined constants which distinguish between different attributes of files.
  304. They are additive, BTW.
  305.  
  306.      File Attribute Constants
  307.      ------------------------
  308.         ReadOnly       $01
  309.         Hidden         $02
  310.         System File    $04
  311.         Volume ID      $08
  312.         Directory      $10
  313.         Archive        $20
  314.         AnyFile        $3F
  315.  
  316. Look at the DOS dir command for an example.  Vary what you type in,
  317. including directory names, and variants (.., \, and just a dirname).
  318. See how it acts.  That's what we want to emulate.  I will lead in
  319. to part 9 by placing something used in planning called pseudocode
  320. here to do such a thing.
  321.  
  322. EXPAND FILENAME ON COMMAND LINE TO FULL DIR, NAME, AND EXTENSION.
  323. IF THE END OF THE FILENAME DOES NOT HAVE A \ THEN
  324.     GET FILE ATTRIBUTE.
  325.     IF NO DOSERROR AND THE FILE IS A DIRECTORY
  326.        ADD A \ TO THE END OF THE FILENAME.
  327. END-IF.
  328. SPLIT FILENAME INTO PROPER DIR, NAME AND EXT.
  329. IF NO NAME, THEN PLACE A * THERE.
  330. IF NO EXTENSION, THEN PLACE A .* THERE.
  331. FULL FILENAME = DIR, NAME, AND EXTENSION TOGETHER.
  332.  
  333. You will have to interpret this into viable code for the programming
  334. problem at the end of the part.
  335.  
  336. Listing a Sequence of Files
  337. ===========================
  338. Now, to answer the question, what if we want to actually do something
  339. with multiple files (specified with * and ?), instead of just one file
  340. (if we want just one file, if we do the i/o checking on reset or
  341. rewrite, we will get an error if they use * or ? in the name.).
  342.  
  343. We need to define the usage of a record as a searchrec type for DOS
  344. (or TSearchRec for WinDos).
  345.  
  346.       searchrec = record
  347.          fill: array[1..21] of byte;
  348.          attr: byte;       { additive from the file attribute constants }
  349.          time: longint;    { Packed Time }
  350.          size: longint;    { Size of file }
  351.          name: string[12]; { name of file }
  352.       end;
  353.  
  354. This record, as well as DateTime is defined in the DOS unit, and we don't
  355. NEED to define these in our programs...They are used with the FindFirst
  356. and FindNext procedures, which are demoed below, listing all filenames
  357. in the current directory.
  358.  
  359. program tutorial19; uses dos;
  360.   var
  361.     fileinfo: searchrec;
  362.   begin
  363.     findfirst('*.*', $37, fileinfo);
  364.     while doserror = 0 do         { 18 = no more files }
  365.       begin
  366.         writeln(fileinfo.name);
  367.         findnext(fileinfo);
  368.       end;
  369.   end.
  370.  
  371. As a note: . and .. are filenames.  . is the base of the file system, ..
  372. is the next directory up....
  373.  
  374. Executing a Program
  375. ===================
  376. You can execute a program from your program by using the exec procedure.
  377. You also have to use the $M compiler directive.  It's a directive to
  378. determine the stack size, as well as the minimum and maximum heap size.
  379. You must set this for to get the memory to run the program.  The format
  380. of the $M compiler directive is this:
  381.  
  382.   {$M <stacksize>,<minheapsize>,<maxheapsize>}
  383.  
  384. If you don't set this, maxheapsize defaults to all of your conventional
  385. memory, definitely not good if we want to give the memory to a program
  386. to even run (nothing will happen if the program doesn't have the memory
  387. to run).  For example, we will use {$M $4000,0,0}.  Here is the format
  388. of the EXEC command.
  389.  
  390.   exec(<command interpreter path>, <command>);
  391.  
  392. command interpreter path -> Where is COMMAND.COM?  We can use getenv
  393. to get the environment variable named COMSPEC to get this.  I've had
  394. people try to rebunk me to say that you could just say exec(<command>);.
  395. It DOES NOT WORK! (the compiler says something about expecting a ,.
  396. You must do it this way!) -- I'm only trying to head off this issue.
  397.  
  398. command -> This must be /C + whatever command you call....
  399.  
  400. Another command used in combination with this is called swapvectors.
  401. It is a parameterless procedure which makes sure our program we call
  402. doesn't literally stomp all over anything we may have done with the
  403. system.  We call it before and after our exec procedure.
  404.  
  405. Here's an example:
  406.  
  407. {$M $4000,0,0}
  408. program tutorial20; uses dos;
  409.   var
  410.     command: string;
  411.   begin
  412.     write('Enter a DOS command: ');
  413.     readln(command);
  414.     command := '/C' + command;
  415.     { we must put the /C there to satisfy COMMAND.COM }
  416.     swapvectors;
  417.     exec(getenv('COMSPEC'), command);
  418.     swapvectors;
  419.     if doserror <> 0 then
  420.       writeln('Dos Error ', doserror, ' occurred.')
  421.     else
  422.       writeln('Successful shell to DOS.');
  423.   end.
  424.  
  425. Use DosExitCode in addition to doserror to determine the results of the
  426. exec'd procedure.  This function returns a word, which we have to use
  427. the HI and LO functions on.  LO code results correspond to errorlevel
  428. returned out of DOS (remember batch file programming?).  A list of HI
  429. code results follow:
  430.  
  431.             0       Normal Termination
  432.             1       Terminated by Ctrl+C
  433.             2       Terminated by Device Error
  434.             3       Terminated as a TSR.
  435.  
  436. Conclusion
  437. ==========
  438. We have covered the major issues in DOS file commands, as well as
  439. listing some of the straight-forward commands that are in Pascal
  440. to work with DOS.  If you looked those up, you would have found that
  441. most of those things are pretty straight-forward.  Here is a practice
  442. programming problem that duplicates the function of the DIR command
  443. in DOS, showing us files with specified information.  It will be a
  444. clone with different functions, though.  Let us see....
  445.  
  446. Practice Programming Problem #8
  447. ===============================
  448.         Write a program in Pascal and entirely Pascal that will show
  449. us a listing of all files in a directory given on the command-line.
  450. It should also support command line parameters that change function,
  451. such as '/?' and '/P'.  Those are the two command-line parameters that
  452. we should support (be sure we make it case insensitive).  /? or -?
  453. will show us help and a listing of who wrote the program and then
  454. terminate the program.  /P or -p should make it so we pause the screen
  455. output on each page.  It also should handle any error-checking that
  456. it may need to perform as presented in this part.
  457.  
  458. Functions:
  459. 1) Show us for each filename on one line, a size, file attributes, date
  460. and time.
  461. 2) For the whole listing of files, show us: The volume label, Size of the
  462. drive we listed, bytes free on the drive we listed, total number of
  463. files listed, total number of directories listed, and DOS version that
  464. is being used at the current time.
  465. 3) All integers or longints > 999 should be delinated by commas, or
  466. periods, whatever you use.
  467. 4) Write r for read-only, a for archive, s for system, h for hidden.
  468. Put the proper ones that apply by each file that it lists.
  469. 5) For reading command-line parameters, be sure to make the order
  470. non-specific.  For example, MYDIR c:\windows /p and MYDIR /p c:\windows
  471. should accomplish the same thing, viewing the windows directory with
  472. page pausing.
  473.  
  474. Hints:
  475. 1) If a file with a volumeID attribute exists in the main directory of
  476. a drive, the name of the file is the volumeID of the drive.
  477. 2) Work with the number as a string and go from the right end to the
  478. left end to place commas as strings for function point 3.
  479. 3) You may want to use a designed record type to hold the final data
  480. you are going to write as you go obtain it to make things easier.
  481. 4) This DIR listing command you are writing should function from the
  482. command-line with regards to filespec exactly like the DOS dir
  483. command.  Check this one by playing around with the DOS dir command.
  484. 5) Hint for the #5 function.  Hold the parameter strings in an array
  485. and write some code to differentiate between command-line params and
  486. the actual path you want to view.
  487. 6) For the string with the attributes, build your string.  You can
  488. directly address and build a string with certain portions as long
  489. as you start with a valid length of something.  I will explain this
  490. in the next part.
  491.  
  492. Sample output for yourdir command.
  493. ----------------------------------
  494. C:\>mydir /?
  495.  
  496. MYDIR (c) 1996 by Glenn Grotzinger.  { put your name here, of course }
  497.  
  498. Help:
  499.   MYDIR <filespec> /<parameters>
  500.   filespec is the filename/dirname(s) we want to list.
  501.   parameters are ? or P
  502.        ? --> this help.
  503.        P --> pause on each screen page.
  504.  
  505. C:\>mydir
  506.  
  507. MYDIR (c) 1996 by Glenn Grotzinger.
  508.  
  509. File listing for: C:\*.*
  510.  
  511. .                                                  [DIR]
  512. ..                                                 [DIR]
  513. CONFIG.SYS              122    12-12-95    03:45pm  rash
  514. DESCRIPT.ION            182    12-28-95    12:14am  -a--    
  515. ARCHIVE.ZIP      14,432,322    03-24-93    09:15pm  r--h  
  516.  
  517. Volume label:          Total files: 3      Total dirs: 2
  518. DOS Version: 6.22
  519.  
  520. 14,432,626 bytes.
  521. 214,232,123 bytes used out of 543,212,123 total bytes.
  522.  
  523. You get the general idea....BTW, for me, this one is 310 lines..
  524. The next practice program given will be in part 10.  This one
  525. and the one in part 10 will be the longest and probably most
  526. complex ones in the whole set of pascal tutorials.  It would be
  527. good for ANYBODY who wants the practice to do these.
  528.  
  529. Next time
  530. =========
  531. We will do the first part of the tutorial covering applications
  532. development.  In doing that, we will develop this DIR equivalent
  533. that I posed above.  Do practice, though, and do this one.  Any
  534. comments, questions, etc, may go to ggrotz@2sprint.net.
  535.